/*
 * Copyright (c) 2011 Sapienza University of Rome.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the
 *   distribution.
 * - Neither the name of the Sapienza University of Rome nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SAPIENZA
 * UNIVERSITY OF ROME OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @author Ugo Colesanti <colesanti@dis.uniroma1.it>
 * @version 1.01 (October 10, 2011)
 *
 */
#warning "********* DISSense Mode - NODE *************"
#ifdef RELAY_NODE
#warning "********* RELAY NODE *************"
#endif
#include "DebugLogger.h"
module NodeApplicationC{

  provides{
	  interface StdControl ;
  }
  uses {
    // Interfaces for initialization:
#ifndef TOSSIM
    interface Boot;
#endif
    interface SplitControl as RadioControl;
    interface StdControl as ManagerControl ;

    interface Send;
    interface Receive ;

    interface Leds;

	interface Timer<TMilli> as Timer ;

	interface FlashLog ;

	interface Read<uint16_t> as ReadVoltage ;
//	interface Read<uint16_t> as ReadSensor ;
	interface SerialSensorRead as ReadSensor ;

	interface DISSenseSampling ;
	interface DISSenseStatistics ;

	interface DebugLogger ;
  }

}

implementation {

	task void startAcquisition() ;

	task void voltageRead() ;
	task void serialSensorRead() ;
	task void logData() ;

	task void sendPacket() ;
	task void startApplication() ;

	uint16_t seqno ;
	app_data_t app_data_buf ;
	message_t app_msg_buf ;
	bool send_busy = FALSE ;
	bool sensing_busy = FALSE ;

	bool started = FALSE ;


	command error_t StdControl.start(){
		seqno = 0 ;
		started = TRUE ;
		if (call RadioControl.start() != SUCCESS) {
			// do nothing
		}
		return SUCCESS ;
	}

	command error_t StdControl.stop(){
		started = FALSE ;
		return SUCCESS ;
	}

#ifndef TOSSIM
	event void Boot.booted(){
		seqno = 0 ;
		started = TRUE ;
		if (call RadioControl.start() != SUCCESS) {
			// do nothing
		}
	}
#endif

	event void RadioControl.startDone(error_t error){
		if(started){
			if (error != SUCCESS){
				return ; // do nothing
			}
			post startApplication() ;
		}
	}

	task void startApplication(){
		dbg("Application","Node application started\n") ;

		call ManagerControl.start() ;


		if (TOS_NODE_ID != 0){
#ifndef RELAYNODE
			call DISSenseSampling.setSamplingOffset(SAMPLING_OFFSET) ;

			//call LogWrite.erase() ;
#endif
		}
	}

	task void startAcquisition(){
		// initialize the data structure and begin the sensor readings.
		uint8_t readings_count ;

//		call DebugLogger.storeDbg(APP_START_ACQUISITION) ;
//		printf("Begin sensing\n") ;
		atomic{
			if(sensing_busy == TRUE){
//				printf("Skipping sensing\n") ;
				return ;
			}
			app_data_buf.seqno = seqno ;
			app_data_buf.timestamp = call Timer.getNow() ;
			app_data_buf.voltage = 0xffff ;
			app_data_buf.sensor_timer = 123456789 ;
			for(readings_count = 0; readings_count < SENSOR_COUNT; readings_count++){
				app_data_buf.sensor[readings_count].data = 0xffff ;
				app_data_buf.sensor[readings_count].temp = 0xffff ;
			}
		}
//		printfflush() ;

		post voltageRead() ;

	}

	event void ReadVoltage.readDone(error_t result,uint16_t value){
		if(started){
//			call DebugLogger.storeDbgVal(APP_READ_VOLTAGE_DONE,result) ;
			if(result == SUCCESS){
					app_data_buf.voltage = value ;
			}
			post serialSensorRead() ;
		}
	}

	event void ReadSensor.readDone(error_t result,serial_sensor_data_t* value){
		uint8_t readings_count ;
		if(started){
	//		call DebugLogger.storeDbgVal(APP_READ_SENSOR_DONE,result) ;
			if(result == SUCCESS){
				atomic{
					app_data_buf.sensor_timer = value->sensor_timer ;
					for(readings_count = 0; readings_count < SENSOR_COUNT; readings_count++){
						app_data_buf.sensor[readings_count].data = value->sensor[readings_count].data ;
						app_data_buf.sensor[readings_count].temp = value->sensor[readings_count].temp ;
					}
				}
			}
			sensing_busy = FALSE ;
	//		printf("Stop sensing\n") ;
	//		printfflush() ;
			post logData() ;
		}
	}


	event void Timer.fired(){
//		call DebugLogger.storeDbg(APP_TIMER_FIRED) ;
		post startAcquisition() ;
	    call Timer.startOneShot(call DISSenseStatistics.getSP()*1024UL) ;
	}


	event void RadioControl.stopDone(error_t error){}


	task void voltageRead(){
		// Try to read voltage. If the request fails, directly read from serial sensor.
		if(call ReadVoltage.read() != SUCCESS){
//			call DebugLogger.storeDbg(APP_READ_VOLTAGE_FAILED) ;
			post serialSensorRead() ;
		}
    }

	task void serialSensorRead(){
		// Try to read data from the serial sensor. If the request fails, directly log the data.
		if(call ReadSensor.read() != SUCCESS){
			sensing_busy = FALSE ;
//			call DebugLogger.storeDbg(APP_READ_SENSOR_FAILED) ;
//			printf("Stop sensing\n") ;
//			printfflush() ;
			post logData() ;
		}

	}

	task void logData(){
		// try to log the data, if the call fails, directly send the packet.
		if(call FlashLog.log(&app_data_buf,sizeof(app_data_buf)) != SUCCESS){
//			call DebugLogger.storeDbg(APP_LOG_DATA_FAILED) ;
			post sendPacket() ;
		}
	}

	  event void FlashLog.logDone(void* msg, error_t result){
		  if(started){
//			  call DebugLogger.storeDbg(APP_LOG_DATA_DONE) ;
			  post sendPacket() ;
		  }
	  }

	  event void FlashLog.eraseDone(error_t error){
		  if(started){
			  if(error == SUCCESS) call Leds.led2On() ;
		  }
	  }


	  task void sendPacket(){
//		  call DebugLogger.storeDbg(APP_SEND_PACKET) ;
		  if(!send_busy){
			  uint8_t datareadings ;
			  call DebugLogger.storeDbg(APP_SEND_PACKET_NSBUSY) ;
			  atomic{
				  app_data_t* app_data = (app_data_t *) call Send.getPayload(&app_msg_buf, sizeof(app_data_t));
				  if(app_data != NULL){

					  app_data->seqno = app_data_buf.seqno ;
					  app_data->voltage = app_data_buf.voltage ;
					  app_data->timestamp = app_data_buf.timestamp ;
					  app_data->sensor_timer = app_data_buf.sensor_timer ;
					  for(datareadings = 0; datareadings < SENSOR_COUNT; datareadings++){
						  app_data->sensor[datareadings].data = app_data_buf.sensor[datareadings].data ;
						  app_data->sensor[datareadings].temp = app_data_buf.sensor[datareadings].temp ;
					  }

					  if (call Send.send(&app_msg_buf, sizeof(app_data_t)) == SUCCESS){
//						  call DebugLogger.storeDbg(APP_SEND_PACKET_OK) ;
						  send_busy = TRUE ;
					  }
					  else{
//						  call DebugLogger.storeDbg(APP_SEND_PACKET_FAILED) ;
//						  post sendPacket() ; //=========================================================> is it better to retry or just skip the send?
						  seqno++ ;
					  }


				  }
			  }
		  }
		  else{
			  seqno++ ;
		  }
	 }

	  event void Send.sendDone(message_t* msg, error_t error) {
		  if(started){
//			  call DebugLogger.storeDbgVal(APP_SEND_PACKET_DONE,error) ;
			  if(error == SUCCESS) call Leds.led2Toggle() ;
			  else call Leds.led1Toggle() ;
			  atomic{
				  send_busy = FALSE ;
				  seqno++ ;
			  }
		  }
	  }

	  event message_t* Receive.receive(message_t* msg, void *payload, uint8_t len) {return msg;} // Should be removed

	  event void DISSenseSampling.startSampling() {
		  dbg("Application","DISSenseSampling start sampling\n") ;
#ifndef RELAY_NODE
		  if(started){
	//		  call DebugLogger.storeDbg(APP_START_SAMPLING) ;
			  if(!send_busy){ // If I was in out of sync, it is possible that the sampling has already been performed and that there is no need to perform another sampling task.
	//			  call DebugLogger.storeDbg(APP_START_SAMPLING_NSBUSY) ;
				  call Leds.led1Toggle() ;
				  post startAcquisition() ;
			  }
	//		  else{
	//			  printf("Already sensed\n") ;
	//			  printfflush() ;
	//		  }
			  call Timer.startOneShot(call DISSenseStatistics.getSP()*1024UL) ;
			  // here I have to start a timer with the sampling period of DISSense in order to schedule a sampling task if I'am not ready.
		  }
#endif
	  }



	  event void DISSenseSampling.syncNotification(bool inSync){ // always signal the status of inSync such that I can delete the samplingtimer (wait for startSampling)
		  dbg("Application","Sync notification received\n") ;
		  if(started){
		  //		  call DebugLogger.storeDbgVal(APP_SYNC_NOTIFICATION,inSync) ;
		  if(inSync){
			  dbg("Application", "InSync\n") ;
			  if(call Timer.isRunning()){
				  dbg("Application","Deleting timer\n") ;
				  call Timer.stop() ;
//				  printf("Sampling timer stopped\n") ;
				  // Unless I am really unlucky (if the drift of the sampling timer is so high, that even if it is running, the startSampling has already passed), I can just wait for a startSampling event
			  }
//			  else{
//				  printf("Sampling timer not running\n") ;
//				  // If send_busy is true, this means that I already performed the sensing task -> the startSampling event will be filtered by the send_busy -> do nothing
//
//				  // If send_busy is not true, this means that the sensing task is running -> filter the startAcquisition in some way -> done with sensing_busy -> do nothing
//
//			  }
		  }
//		  else{
//			  printf("Out of sync\n") ;
//			// I just let the sampling timer firing when scheduled
//		  }
//		  printfflush() ;
		  }
	  }

}
